﻿/*
 *       This file is part of VFC.
 *
 *        VFC is free software: you can redistribute it and/or modify
 *        it under the terms of the GNU General Public License as published by
 *        the Free Software Foundation, either version 3 of the License, or
 *        (at your option) any later version.
 *
 *        VFC is distributed in the hope that it will be useful,
 *        but WITHOUT ANY WARRANTY; without even the implied warranty of
 *        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *        GNU General Public License for more details.
 *
 *        You should have received a copy of the GNU General Public License
 *        along with VFC.  If not, see <http://www.gnu.org/licenses/>.
 */


using System;
using System.Collections.Generic;
using System.Threading;
//using System.Linq;
using System.Text;
using VFC.Nes.Cpu;
using VFC.Nes.Mapper;
using VFC.Config;
using VFC;

namespace VFC.Emu
{
    class EmuCore
    {
        public static RomReader rom;
        private static Thread CoreThread;
        private static bool Running = false;

        private static int PrevCpuCycle = 0;

        public static void Init()
        {
            //Initialize Debugger...
            Console.WriteLine("Initializing debugger");
            Debug.Debugger.Init();
            //Initialize Nes Hardware here...
            Nes.Cpu.Memory.Clear();
            Console.WriteLine("Initializing Cpu memory");
            Nes.Cpu.Memory.Init();
            Console.WriteLine("Initializing Cpu register");
            Nes.Cpu.Register.Init();
            Console.WriteLine("Initializing Cpu core");
            Nes.Cpu.Core.Init();
            Console.WriteLine("Initializing Ppu memory");
            Nes.Ppu.Memory.Clear();

            if (App.EnableOpcodeLogging)
            {
                Console.WriteLine("\nOpcode Logging -> On");
            }
        }

        public static void LoadRom(string path)
        {
            rom = new RomReader(path);
            Console.Title = VFC.Config.App.ConsoleTitle + " - " + rom.RomFileName();

            if (rom.IsNes())
            {
                Console.WriteLine("\nLoading file '" + rom.RomFileName() + "'\n");
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("  16Kb Rom Bank        :" + rom.Count16KbRomBank());
                Console.WriteLine("  8Kb VRom Bank        :" + rom.Count8KbVRomBank());
                Console.WriteLine("  512Byte Trainer      :" + rom.Is512ByteTrainerPresent().ToString());
                Console.WriteLine("  Mirroring            :" + rom.Mirroring());
                Console.WriteLine("  Battery Backed       :" + rom.IsBatteryBacked().ToString());
                Console.WriteLine("  4 Screen VRam Layout :" + rom.Is4ScreenLayout().ToString());
                Console.WriteLine("  VS-System            :" + rom.VsSystem().ToString());
                Console.WriteLine("  Region               :" + rom.Region());
                Console.WriteLine("  8Kb Ram Bank         :" + rom.Count8KbRamBank());

                switch (rom.MapperNumber())
                {
                    case 0: //Mapper 0: NROM
                        Console.WriteLine("  Mapper               :" + rom.MapperNumber() + "\n");
                        Console.ResetColor();
                        Nrom nrom = new Nrom(rom);
                        rom.Get16KbRomBank();
                        rom.Get8KbVRomBank();
                        nrom.LoadRomToMemory();
                        nrom.LoadVRomToPpuMemory();
                        Nes.Cpu.Register.PC = Nes.Cpu.Memory.GetResetVector(); //Start Adress: Should be set to ResetVector or 0xc000 for nestest.nes
                        break;
                    default:
                        Console.WriteLine("\nERROR: Unsupported Mapper. '" + rom.MapperNumber().ToString() + "'");
                        break;
                }
            }
            else
            {
                Console.WriteLine("ERROR: Not a nes file.");
            }
        }

        public static void Run()
        {
            Running = true;
            CoreThread = new Thread(new ThreadStart(Execute));
            CoreThread.Start();
            while (!CoreThread.IsAlive) ; //Spinwait until CoreThread is running...
        }

        public static void Stop()
        {
            Running = false;
        }

        public static void Execute()
        {
            while (Running)
            {
                //PPU execution times = [Cpu Opcode Cycle] * 3
                //so if the instruction took 6 cpu cycle, the ppu should run 18 times
                //to catch up with cpu...
                //
                //Scanline-base emulation: run the processor for 1 scanline then let
                //the ppu catch up with cpu...
                //
                //   1 CPU Cycle = 3 PPU Cycles
                //   341 PPU Cycles = 1 Scanline = 113.66666666666666666666666666667 CPU Cycles

                PrevCpuCycle = Nes.Counter.CpuCycle;
                for (int ctr = 0; ctr <= 100; ctr++)
                {
                    Nes.Cpu.Core.Exec();
                    if((Nes.Counter.CpuCycle - PrevCpuCycle) >= 113)
                    {
                        Nes.Counter.CpuCycle = 0;
                        break;
                    }
                }
                Nes.Ppu.Core.Exec();
                //Update Game Pad...
                //Run SPU...
                //Update Debugger...

                Thread.Sleep(1);
            }
        }

        public static void Step()
        {
            for (int ctr = 0; ctr <= 100; ctr++)
            {
                Nes.Cpu.Core.Exec();
                if ((Nes.Counter.CpuCycle - PrevCpuCycle) >= 113)
                {
                    break;
                }
            }
            Nes.Ppu.Core.Exec();
        }
    }
}
